/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright(c) 2007-2022 Jianjun Jiang <8192542@qq.com>
 *          Official site: http://xboot.org
 *          Mobile phone: +86-18665388956
 *          QQ: 8192542
 *
 * Contains: nxos file system.
 *
 * Change Logs:
 * Date           Author            Notes
 * 2022-3-26      JasonHu           Port from xboot
 */

#include <vfsc.h>
#include <nxos/ipc.h>
#include <nxos/mman.h>
#include <nxos/signal.h>

#if 0
#define DEBUG_LOG(...) printf(__VA_ARGS__)
#else
#define DEBUG_LOG(...)
#endif

/* bit 30 is dir fd flags */
#define DIR_FD_BITS (1U << 30)
#define FILE_FD_MASK (DIR_FD_BITS - 1)
#define FILE_FD(fd) ((fd) & FILE_FD_MASK)
#define IS_DIR_FD(fd) ((fd) & DIR_FD_BITS)

void FileCloseSignalHandler(NX_SignalInfo * info)
{
    long fd;
	NX_Error err;
	
    if (info->signal != NX_SIGNAL_UOBJECT)
    {
        DEBUG_LOG("uobj %d not a socket!\n", info->signalValue);
        return;
    }
    fd = (long)info->signalValue;

	if (IS_DIR_FD(fd))
	{
		DEBUG_LOG("VFS: do close dir fd:%x\n", fd);
		fd = FILE_FD(fd);
		err = VFS_DirClose(fd);
	}
	else
	{
		DEBUG_LOG("VFS: do close file fd:%x\n", fd);
		err = VFS_FileClose(fd);
	}
    if (err != NX_EOK) {
        DEBUG_LOG("VFS: do close err %d fd:%d\n",
            err, fd);
    } else {
        DEBUG_LOG("VFS: do close fd:%d\n", fd);
    }
}

NX_Size VFS_Dispatch(NX_IpcMsg *ipcMsg)
{
	VFS_IpcReq *fr = NX_IpcGetMsgData(ipcMsg);
	DEBUG_LOG("vfs: requst type:%d\n", fr->type);
	switch (fr->type)
	{
	case VFS_REQ_OPEN:
	{
		struct VFS_OpenArgs *args = &fr->vfs_open_args;
		NX_Error err;
		int fd = VFS_FileOpen(args->path, args->flags, args->mode, &err);
		if (fd < 0)
		{
			return -1;
		}
		// if (fd != NX_EOK)
		// {
		// 	return -err;
		// }
        int solt = NX_SoltInstall(ipcMsg->clientSlot, (void *)(fd));
        DEBUG_LOG("VFS: install object solt: %d, fd:%d\n", solt, fd);
        if (solt == NX_SOLT_INVALID_VALUE)
        {
            VFS_FileClose(fd);
            return -1;
        }
		return solt;
	}
	case VFS_REQ_CLOSE:
	{
		struct VFS_CloseArgs *args = &fr->vfs_close_args;
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do close invalid fd:%d\n",
                args->solt);
    		return NX_EINVAL;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return NX_EINVAL;
        }

		if (IS_DIR_FD(fd))
		{
    		return NX_EPERM;
		}

		return NX_SoltUninstall(ipcMsg->clientSlot, args->solt);
	}

	case VFS_REQ_READ:
	{
		struct VFS_ReadArgs *args = &fr->vfs_read_args;
		NX_Error err;
		NX_Solt shmMap = NX_SOLT_INVALID_VALUE;
		char *bufRead = args->buf;

		NX_Solt shm = NX_IpcGetMsgSolt(ipcMsg, 0);
		if (shm != NX_SOLT_INVALID_VALUE)
		{
			bufRead = NX_ShareMemMap(shm, &shmMap);
			if (!bufRead) {
				return 0;
			}
		}
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do read invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		NX_U64 size = VFS_FileRead(fd, bufRead, args->len, &err);
		if (bufRead)
		{
			NX_ShareMemUnmap(shmMap);
		}

		// if (err != NX_EOK)
		// {
		// 	return -err;
		// }
		return size;
	}
	case VFS_REQ_WRITE:
	{
		struct VFS_WriteArgs *args = &fr->vfs_write_args;
		NX_Error err;
		NX_Solt shmMap = NX_SOLT_INVALID_VALUE;
		char *bufWrite = args->buf;

		NX_Solt shm = NX_IpcGetMsgSolt(ipcMsg, 0);
		if (shm != NX_SOLT_INVALID_VALUE)
		{
			bufWrite = NX_ShareMemMap(shm, &shmMap);
			if (!bufWrite) {
				DEBUG_LOG("VFS: NX_ShareMemMap\n");
				return 0;
			}
		}
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do write invalid solt:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
			DEBUG_LOG("VFS: do write invalid fd:%d\n", fd);
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
			DEBUG_LOG("VFS: do write invalid file:%d\n", fd);
    		return -1;
		}

		NX_U64 size = VFS_FileWrite(fd, bufWrite, args->len, &err);
		if (bufWrite)
		{
			NX_ShareMemUnmap(shmMap);
		}
		// if (err != NX_EOK)
		// {
		// 	return -err;
		// }
		return size;
	}
	case VFS_REQ_IOCTL:
	{
		struct VFS_IoctlArgs *args = &fr->vfs_ioctl_args;
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do ioctl invalid fd:%d\n",
                args->solt);
    		return NX_EINVAL;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return NX_EINVAL;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		return VFS_FileIoctl(fd, args->cmd, args->arg);
	}
	case VFS_REQ_LSEEK:
	{
		struct VFS_LseekArgs *args = &fr->vfs_lseek_args;
		NX_Error err;

		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do lseek invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		NX_Offset offset = VFS_FileSetPointer(fd, args->off, args->whence, &err);
		// if (err != NX_EOK)
		// {
		// 	return -err;
		// }
		return offset;
	}
	case VFS_REQ_FSYNC:
	{
		struct VFS_FsyncArgs *args = &fr->vfs_fsync_args;
		
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do fsync invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		return VFS_FileSync(fd);
	}
	case VFS_REQ_FCHMOD:
	{
		struct VFS_FchmodArgs *args = &fr->vfs_fchmod_args;
				
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do fchmod invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		return VFS_FileSetMode(fd, args->mode);
	}
	case VFS_REQ_FSTAT:
	{
		struct VFS_FstatArgs *args = &fr->vfs_fstat_args;
		
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do fstat invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (IS_DIR_FD(fd))
		{
    		return -1;
		}

		return VFS_FileGetStat(fd, &args->st);
	}
	case VFS_REQ_OPENDIR:
	{
		struct VFS_OpenDirArgs *args = &fr->vfs_opendir_args;
		NX_Error err;
		int fd = VFS_DirOpen(args->name, &err);
		// if (err != NX_EOK) {
		// 	return -err;
		// }
		if (fd < 0)
		{
			return -1;
		}

		int solt = NX_SoltInstall(ipcMsg->clientSlot, (void *)((fd) | DIR_FD_BITS));
        DEBUG_LOG("VFS: install dir object solt: %d, fd:%d\n", solt, fd);
        if (solt == NX_SOLT_INVALID_VALUE)
        {
			VFS_DirClose(fd);
            return -1;
        }
		return solt;
	}
	case VFS_REQ_CLOSEDIR:
	{
		struct VFS_CloseDirArgs *args = &fr->vfs_closedir_args;
		
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do closedir invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return NX_EINVAL;
        }

		if (!IS_DIR_FD(fd))
		{
    		return NX_EPERM;
		}

		// VFS_DirClose
		return NX_SoltUninstall(ipcMsg->clientSlot, args->solt);
	}
	case VFS_REQ_READDIR:
	{
		struct VFS_ReadDirArgs *args = &fr->vfs_readdir_args;
				
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do readdir invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
			DEBUG_LOG("VFS: do readdir invalid fd:%x\n",
                fd);
    		return -1;
        }

		if (!IS_DIR_FD(fd))
		{
			DEBUG_LOG("VFS: do readdir invalid dir:%x\n",
                fd);
    		return -1;
		}

		return VFS_DirRead(FILE_FD(fd), &args->dir);
	}
	case VFS_REQ_REWINDDIR:
	{
		struct VFS_RewindDirArgs *args = &fr->vfs_rewinddir_args;
				
		if (args->solt < 0)
        {
            DEBUG_LOG("VFS: do rewinddir invalid fd:%d\n",
                args->solt);
    		return -1;
        }

		long fd = (long)NX_SoltObject(ipcMsg->clientSlot, args->solt);
        if (fd < 0)
        {
    		return -1;
        }

		if (!IS_DIR_FD(fd))
		{
    		return -1;
		}

		return VFS_DirResetPointer(FILE_FD(fd));
	}
	case VFS_REQ_MKDIR:
	{
		struct VFS_MkdirArgs *args = &fr->vfs_mkdir_args;
		return VFS_DirCreate(args->path, args->mode);
	}
	case VFS_REQ_RMDIR:
	{
		struct VFS_RmDirArgs *args = &fr->vfs_rmdir_args;
		return VFS_DirDelete(args->path);
	}
	case VFS_REQ_RENAME:
	{
		struct VFS_RenameArgs *args = &fr->vfs_rename_args;
		return VFS_FileRename(args->src, args->dst);
	}
	case VFS_REQ_UNLINK:
	{
		struct VFS_UnlinkArgs *args = &fr->vfs_unlink_args;
		return VFS_FileDelete(args->path);
	}
	case VFS_REQ_ACCESS:
	{
		struct VFS_AccessArgs *args = &fr->vfs_access_args;
		return VFS_FileAccess(args->path, args->mode);
	}
	case VFS_REQ_CHMOD:
	{
		struct VFS_ChmodArgs *args = &fr->vfs_chmod_args;
		return VFS_FileSetModeToPath(args->path, args->mode);
	}
	case VFS_REQ_STAT:
	{
		struct VFS_StatArgs *args = &fr->vfs_stat_args;
		return VFS_FileGetStatFromPath(args->path, &args->st);
	}

	default:
		break;
	}
}
